#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <cstdlib>
#include <new>
#include <typeinfo>
#include <list>
#include <thread> // std::thread
#include <mutex>  // std::mutex

using namespace std;

// 参考: https://blog.csdn.net/misayaaaaa/article/details/53786215
// 参考: https://github.com/applenob/Cpp_Primer_Practice/blob/master/excersize/ch19.md

// chapter 19 特殊工具与技术 


// 1.
// 使用 malloc 编写你自己的 operator new(sizt_t)函数，使用 free 编写operator delete(void *)函数

void *operator new(std::size_t n){  
    std::cout << "new(size_t)"<<endl;  
    if (void *mem = malloc(n))  
        return mem;  
    else  
        throw std::bad_alloc();  
}  
void operator delete(void *mem) noexcept{  
    std::cout << "delete(void*)<<endl";  
    free(mem);  


1：知识点1：new和delete运算符的实现细节：

// new的操作过程：第一步、new表达式调用operator new(或者 operator new[])的标准库函数，该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象（或数组）
// 第二步、编译器运行相应的构造函数以构造这些对象，并为其传入初始值。第三步、对象分配了空间并构造完成，返回一个指向该对象的指针
// delete的操作过程：第一步：对指针所指对象的数组指针所指数组执行相应的析构函数。第二步：编译器调用operator delete（或者operator delete[]）的标准库函数释放内存空间
知识点2：在自定义这两个标准库函数的时候，一定要弄对，因为这对计算机来说至关重要！
知识点3：重载new和delete，其实就是重载标准库的相关的两个operator函数
知识点4：实现的过程：若分配（释放）的对象是类类型，则编译器首先在类及其基类的作用域中查找，若该类中含有operator new成员或者operator delete成员，则相应表达式将调用这些成员，
否则，编译器将在全局作用域查找匹配函数，若找到用户自定义版本，则执行，找不到，会使用标准库版本
知识点5：我们为一个对象分配空间时使用operator new，为一个数组分配空间时使用operator new[]
知识点6：C++从C语言中继承了malloc和free函数，头文件为cstdlib，malloc接受一个表示待分配字节数的size_t，返回指向该内存空间的指针或者返回0表示分配失败。
free()函数接受一个void *，它是malloc返回指针的副本，free将相关内存返回给系统，free(0)无意义。所以operator new可以用malloc来实现


// 2.




// 3.
// C
class A { /* ... */};
class B : public A { /* ... */};
class C : public B { /* ... */};
class D : public B, public A { /* ... */};

下面哪个 dynamic_cast 将失败？

(a) A *pa = new C;
	B *pb = dynamic_cast<B*>(pa);
(b) B *pb = new B;
	C *pc = dynamic_cast<C*>(pb);
(c) A *pa = new D;
	B *pb = dynamic_cast<B*>(pa);

（a）成功；
（b）失败，pb是指向B的指针，不能转换为指向C的指针； // pb指向的对象不包含C对象
（c）失败，A *pa = new D具有二义性。 

(a )成功。“pa”的类型(类类型“C”)公共派生自目标类型“B”。
(b )失败。“pb”类型(类类型“B”)是目标类型“C”的公共基类。“pc”将等于nullptr。
(c )失败。A *pa = new D;A对于D具有二义性。将’D’指针转换为’ A 指针是不允许的

// 4.
// A *pa = new C;
使用上一个练习定义的类改写下面的代码，将表达式*pa 转换成类型C&：

if (C *pc = dynamic_cast<C*>(pa))
{
	//使用C的成员
} else {
	//使用A的成员
}


// 5.
// 在什么情况下你应该用 dynamic_cast 替代虚函数？
// 前面有说到并非任何时候都能有虚函数，假设有一个基类的指针指向其派生类，派生类中有一个成员基类中没有，这时候想通过这个基类的指针来调用这个成员就是不可以的，此时可以用dynamic_cast



// 6.
// 7.
// 8.


// 9.


// 10.
// 已知存在如下的类继承体系，其中每个类定义了一个默认公有的构造函数和一个虚析构函数。下面的语句将打印哪些类型名字？

class A { /* ... */ };
class B : public A { /* ... */ };
class C : public B { /*...*/ };
(a) A *pa = new C;
	cout << typeid(pa).name() << endl;
(b) C cobj;
	A& ra = cobj;
	cout << typeid(&ra).name() << endl;
(c) B *px = new B;
	A& ra = *px;
	cout << typeid(ra).name() << endl;

a、class A* 指针类型
b、class A* 引用类型
c、class B 引用指向的类型




// 11.
// 12.

// 13.


// 14.




// 15.
// 普通函数指针和指向成员函数的指针有何区别？





// 26.
// C语言不支持重载，因此C链接提示只能说明重载函数中的一个


int main()
{
   
    printf("hello c++ \n");
}